#include "../include/head.h"
#include "../include/thread_pool.h"

//信号处理函数，实现异步退出线程池
//父进程收到信号后，通知子进程处理

// 父进程：信号处理 + 进程管理
// 子进程：业务处理 + 线程池管理

int exitpipe[2]; //父子进程通信管道

void sig_handle(int sig_num){
    printf("已收到信号 %d !\n",sig_num);
    write(exitpipe[1],&sig_num,4); 

}

int main(int argc,char *argv[]){
    //参数：配置文件路径
    if(argc!=2){
        fprintf(stderr,"args error!\n");
        return -1;
    }
    
    pipe(exitpipe);//建立父子进程通信的管道，父进程写exitpipe[1],子进程读exitpipe[0];

    // 1.父进程:用来接收退出信号
    if(fork()){
        printf("父进程的pid为: %d\n",getpid());
        close(exitpipe[0]);
        signal(SIGUSR1,sig_handle); //注册退出信号
        while(1){
            sleep(1);
        }
        wait(NULL); //等待子进程退出
        printf("线程池已关闭，所有子线程都已退出！\n");
        exit(0);
    }
    
    // 2.子进程:管理各种请求：客户端，线程池，退出管理
    printf("子进程的pid为：%d\n",getpid());
    close(exitpipe[1]);
    
    // 2.1从配置文件中拿到 服务器ip、port号、线程数量
    Conf conf;
    //char ip[16]="";
    //int port =0;
    //int thread_num=0;
    conf_get(argv[1],&conf);
    printf("服务器的ip:%s, port:%s, 线程数量：%d\n",conf.host,conf.port,conf.worker_num);

    // 2.2建立tcp监听
    socket_t fd_server =tcp_init(conf.host,conf.port);
    //EXIT_CHECK(fd_server,-1,"socket_init");
    printf("DiskServer[ip:%s,port:%s]\n",conf.host,conf.port);
    
    // 2.3建立线程池并启动
    ThreadPool_t pool;
    init_ThreadPool(&pool,conf.worker_num);
    boot_ThreadPool(&pool);
    printf("线程已启动！\n");

    // 2.4将服务器套接字、退出管道加入epoll管理
    int epfd = epoll_create(1);
    epoll_add(epfd,fd_server); //监听新连接
    epoll_add(epfd,exitpipe[0]); //监听退出信号
    
    // 2.5创建epoll事件数组，准备接收事件
    int read_fd_num=0;          //接收epoll_wait返回的就绪事件数量
    struct epoll_event evs[2];  //事件数组，只监听2个fd（服务器socket+退出管道）
    socket_t fd_client;
    
    while(1){
        read_fd_num = epoll_wait(epfd,evs,2,-1); 
        
        for(int i=0;i<read_fd_num;++i){
            //有新客户端连接时，加入任务队列，通知子线程
            if(evs[i].data.fd == fd_server){
                fd_client = accept(fd_server,NULL,NULL);
                //fd_client是返回新客户端连接的套接字
                printf("客户端已连接到服务端\n");
                pTaskNode_t pNew =(pTaskNode_t)calloc(1,sizeof(TaskNode_t));
                pNew->cli_fd = fd_client;                   //将客户端套接字存入任务节点
                
                pthread_mutex_lock(&pool.que.mutex); //加锁
                push_TaskQueue(&pool.que,pNew);//入队
                pthread_cond_signal(&pool.que.cond);//通知子线程处理
                pthread_mutex_unlock(&pool.que.mutex);//解锁
            }
            //当父进程发来退出通知时，读取管道
            else if(evs[i].data.fd == exitpipe[0]){
                int sig_num;
                read(exitpipe[0],&sig_num,4);
                printf("服务端收到信号%d，准备退出！\n",sig_num);

                //将退出标志置为1，唤醒所有线程，让它们自行退出
                pool.que.flag=1;
                pthread_cond_broadcast(&pool.que.cond);
                
                //关闭线程池，回收线程池资源
                close_ThreadPool(&pool);
                destroy_ThreadPool(&pool);

                printf("服务端已退出！\n");
                //退出程序
                exit(0);
            }

        }

    }
    return 0;
}
